from app.Dao.FinanceDao.IncomeDao import IncomeDAO
from datetime import date
from typing import List, Optional, Dict, Any
from app.Models.FinanceModels.IncomeFirstCate import IncomeFirstCate
from app.Models.FinanceModels.IncomeSecondCate import IncomeSecondCate
import pandas as pd
from datetime import date
import calendar


class IncomeServices:
    """收入服务层，处理收入相关业务逻辑"""

    @staticmethod
    def create_income(income_data: Dict[str, Any], user_id=1):
        """
        创建收入记录（字典参数传入，包含参数校验）
        :param income_data: 收入数据字典，需包含以下键：
                            - user_id: 用户ID（int，正整数）
                            - first_cate_id: 一级分类ID（int，正整数）
                            - second_cate_id: 二级分类ID（int，正整数）
                            - amount: 收入金额（float，正数）
                            - date: 收入日期（date类型）
                            - remark: 备注（str，可选，默认None）
        :return: 包含创建结果的字典
        """
        # 1. 从字典中提取参数（设置默认值，避免可选参数缺失）
        user_id = user_id
        first_cate_id = income_data.get("first_cate_id")
        second_cate_id = income_data.get("second_cate_id")
        amount = float(income_data.get("amount"))
        date_val = income_data.get("date")  # 避免与导入的date类重名
        remark = income_data.get("remark", None)  # 可选参数，默认None

        # 2. 参数校验（逻辑与原版本一致，确保数据合法性）
        # 校验必传字段是否存在
        required_fields = ["first_cate_id", "second_cate_id", "amount", "date"]
        missing_fields = [field for field in required_fields if field not in income_data]
        if missing_fields:
            raise ValueError(f"缺少必填字段：{', '.join(missing_fields)}")

        # 校验字段值合法性
        if amount <= 0:
            raise ValueError("收入金额必须为正数")
        if user_id <= 0 or first_cate_id <= 0 or second_cate_id <= 0:
            raise ValueError("用户ID、一级分类ID、二级分类ID必须为正整数")

        # 3. 调用DAO层创建记录（将提取的参数传入）
        income = IncomeDAO.create_income(
            user_id=user_id,
            first_cate_id=first_cate_id,
            second_cate_id=second_cate_id,
            amount=amount,
            date=date_val,
            remark=remark
        )
        return True, '新增成功'

    @staticmethod
    def get_income_by_id(income_id: int) -> Dict[str, Any]:
        """
        根据ID获取收入记录
        :param income_id: 收入记录ID
        :return: 包含查询结果的字典
        """
        if income_id <= 0:
            raise ValueError("收入ID必须为正整数")

        income = IncomeDAO.get_income_by_id(income_id)
        if not income:
            return {
                "success": False,
                "message": "收入记录不存在"
            }

        return {
            "success": True,
            "data": income.to_dict()
        }

    @staticmethod
    def get_user_incomes(
            user_id: int,
            start_date: Optional[date] = None,
            end_date: Optional[date] = None
    ) -> tuple[bool, Any]:
        """
        获取用户的收入记录（支持日期筛选，附带一级/二级分类名称）
        :param user_id: 用户ID
        :param start_date: 开始日期（可选）
        :param end_date: 结束日期（可选）
        :return: 元组 (是否成功, 数据/错误信息)，数据为带分类名称的收入字典列表
        """
        # 1. 基础参数校验
        if user_id <= 0:
            return False, ValueError("用户ID必须为正整数")
        if start_date and end_date and start_date > end_date:
            return False, ValueError("开始日期不能晚于结束日期")

        # 2. 调用DAO层获取收入记录（含分类ID）
        incomes = IncomeDAO.get_incomes_by_user(
            user_id=user_id,
            start_date=start_date,
            end_date=end_date
        )

        # 3. 批量查询所有用到的分类（减少数据库查询次数，提升效率）
        # 提取收入记录中所有不重复的分类ID
        first_cate_ids = {income.first_cate_id for income in incomes if income.first_cate_id}
        second_cate_ids = {income.second_cate_id for income in incomes if income.second_cate_id}

        # 查询一级分类：{分类ID: 分类名称} 的映射字典
        first_cate_map = {
            cate.id: cate.first_cate_name
            for cate in IncomeFirstCate.query.filter(IncomeFirstCate.id.in_(first_cate_ids)).all()
        }

        # 查询二级分类：{分类ID: 分类名称} 的映射字典
        second_cate_map = {
            cate.id: cate.second_cate_name
            for cate in IncomeSecondCate.query.filter(IncomeSecondCate.id.in_(second_cate_ids)).all()
        }

        # 4. 补充分类名称到收入记录中
        income_with_cate = []
        for income in incomes:
            income_dict = income.to_dict()  # 先获取收入基础信息（含分类ID）
            # 补充一级分类名称（无匹配ID时显示空字符串或提示）
            income_dict["first_cate_name"] = first_cate_map.get(income.first_cate_id, "")
            # 补充二级分类名称
            income_dict["second_cate_name"] = second_cate_map.get(income.second_cate_id, "")
            income_with_cate.append(income_dict)

        # 5. 返回结果（成功标识 + 带分类名称的收入列表）
        return True, income_with_cate

    @staticmethod
    def get_incomes_by_category(
            first_cate_id: Optional[int] = None,
            second_cate_id: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        按分类获取收入记录
        :param first_cate_id: 一级分类ID
        :param second_cate_id: 二级分类ID
        :return: 包含收入列表的字典
        """
        if (first_cate_id is not None and first_cate_id <= 0) or \
                (second_cate_id is not None and second_cate_id <= 0):
            raise ValueError("分类ID必须为正整数（若提供）")

        incomes = IncomeDAO.get_incomes_by_category(
            first_cate_id=first_cate_id,
            second_cate_id=second_cate_id
        )

        return {
            "success": True,
            "count": len(incomes),
            "data": [income.to_dict() for income in incomes]
        }

    @staticmethod
    def update_income(income_id: int, **kwargs) -> Dict[str, Any]:
        """
        更新收入记录
        :param income_id: 收入记录ID
        :param kwargs: 待更新的字段（如amount, date等）
        :return: 包含更新结果的字典
        """
        if income_id <= 0:
            raise ValueError("收入ID必须为正整数")

        # 校验更新字段（示例：金额必须为正数）
        if "amount" in kwargs and float(kwargs["amount"]) <= 0:
            raise ValueError("更新的金额必须为正数")

        updated_income = IncomeDAO.update_income(income_id, **kwargs)
        if not updated_income:
            return {
                "success": False,
                "message": "收入记录不存在或更新失败"
            }

        return {
            "success": True,
            "data": updated_income.to_dict()
        }

    @staticmethod
    def delete_income(income_id: int) -> Dict[str, Any]:
        """
        删除收入记录
        :param income_id: 收入记录ID
        :return: 包含删除结果的字典
        """
        if income_id <= 0:
            raise ValueError("收入ID必须为正整数")

        result = IncomeDAO.delete_income(income_id)
        if not result:
            return {
                "success": False,
                "message": "删除失败，收入记录不存在"
            }

        return {
            "success": True,
            "message": "收入记录已成功删除"
        }

    @staticmethod
    def get_income_summary(user_id: int, year: int, month: Optional[int] = None) -> Dict[str, Any]:
        """
        获取用户的收入汇总（按年/月）
        :param user_id: 用户ID
        :param year: 年份
        :param month: 月份（可选）
        :return: 包含汇总金额的字典
        """
        if user_id <= 0:
            raise ValueError("用户ID必须为正整数")
        if not (1900 <= year <= 2100):
            raise ValueError("年份必须在1900-2100之间")
        if month is not None and not (1 <= month <= 12):
            raise ValueError("月份必须在1-12之间")

        total = IncomeDAO.get_income_summary(
            user_id=user_id,
            year=year,
            month=month
        )

        return {
            "success": True,
            "data": {
                "user_id": user_id,
                "year": year,
                "month": month,
                "total_amount": round(total, 2)  # 保留两位小数
            }
        }

    @staticmethod
    def get_second_cates_by_first_cate(
            first_cate_id: int,
    ):
        """
        根据一级分类ID获取关联的二级分类
        :param first_cate_id: 一级分类ID
        :param user_id: 用户ID（可选）
        :return: 包含二级分类列表的字典
        """
        if first_cate_id <= 0:
            return False, ValueError("一级分类ID必须为正整数")

        second_cates = IncomeDAO.get_second_cates_by_first_cate(
            first_cate_id=first_cate_id,
        )

        return True, second_cates

    @staticmethod
    def get_all_first_cates():
        return True, IncomeDAO.get_all_first_cates()

    @staticmethod
    def get_inc_card(year, month, user_id):
        """
        获取指定年份的收入统计卡片数据（年累计和月累计）
        """
        # 1. 优化日期范围：仅查询目标年份数据，减少数据量
        start_date = date(year, 1, 1)
        # 若查询未来月份，结束日期取当月最后一天；否则取当前日期
        if (year, month) > (date.today().year, date.today().month):
            end_day = calendar.monthrange(year, month)[1]
            end_date = date(year, month, end_day)
        else:
            end_date = date.today()

        # 2. 获取数据并转换为DataFrame（仅目标年份数据）
        inc_data = IncomeDAO.get_incomes_by_user(user_id, start_date, end_date)
        if not inc_data:  # 处理无数据场景
            return True, [
                {'name': '年累计收入', 'value': 0},
                {'name': '月累计', 'value': 0}
            ]

        all_inc_df = pd.DataFrame(inc_data)
        # 统一转换日期列，避免重复计算
        date_series = pd.to_datetime(all_inc_df['date'])
        all_inc_df['year'] = date_series.dt.year
        all_inc_df['month'] = date_series.dt.month

        # 3. 计算年累计（利用已过滤的日期范围，无需再次按年份筛选）
        this_year_inc = all_inc_df['amount'].sum()  # 假设金额字段为amount，避免sum所有列

        # 4. 计算月累计（修复逻辑运算符优先级问题，明确筛选条件）
        month_mask = (all_inc_df['month'] == month) & (all_inc_df['year'] == year)
        this_month_inc = all_inc_df.loc[month_mask, 'amount'].sum()

        # 5. 返回格式化结果（确保数值类型正确）
        return True, [
            {'name': '年累计收入', 'value': float(this_year_inc)},
            {'name': '月累计', 'value': float(this_month_inc)}
        ]

    @staticmethod
    def get_inc_struct(year, month, user_id):
        """
        获取指定年份的收入统计卡片数据（年累计和月累计）
        """
        # 1. 优化日期范围：仅查询目标年份数据，减少数据量
        start_date = date(year, 1, 1)
        # 若查询未来月份，结束日期取当月最后一天；否则取当前日期
        if (year, month) > (date.today().year, date.today().month):
            end_day = calendar.monthrange(year, month)[1]
            end_date = date(year, month, end_day)
        else:
            end_date = date.today()

        # 2. 获取数据并转换为DataFrame（仅目标年份数据）
        inc_data: list = IncomeDAO.get_incomes_by_user(user_id, start_date, end_date)

        all_inc_df = pd.DataFrame(inc_data)
        # 统一转换日期列，避免重复计算
        date_series = pd.to_datetime(all_inc_df['date'])
        all_inc_df['year'] = date_series.dt.year
        all_inc_df['month'] = date_series.dt.month

        # 获取当月的支出数据
        month_data = all_inc_df[(all_inc_df['month'] == month) & (all_inc_df['year'] == year)]
        # 按照一级分类进行分组
        first_cate_group = month_data.groupby('first_cate_name')['amount'].sum().reset_index()
        print(first_cate_group)

        # 5. 返回格式化结果（确保数值类型正确）
        # 转换为 [{'name': '分类名', 'value': 金额}] 格式
        result = [{'name': row['first_cate_name'], 'value': row['amount']} for index, row in
                  first_cate_group.iterrows()]
        print(result)

        return True, result
